﻿/*
 * Erstellt mit SharpDevelop.
 * Benutzer: Kai Patrick
 * Datum: 24.03.2008
 * Zeit: 16:14
 * 
 * Sie können diese Vorlage unter Extras > Optionen > Codeerstellung > Standardheader ändern.
 */

using System;
using Irony.Compiler;

namespace Gamadus.Engine.Compiler
{
  /// <summary>
  /// This class represents Irony Grammar for Script.NET
  /// </summary>
  public class GmlGrammar : Grammar
  {
    public GmlGrammar()
    {
      #region 1. Terminals
      //NumberLiteral n = TerminalFactory.CreateCSharpNumber("number");
      NumberLiteral n = new NumberLiteral("number", TermOptions.SpecialIgnoreCase);
      n.AddPrefixFlag("$", ScanFlags.Hex);

      IdentifierTerminal v = new IdentifierTerminal("Identifier");
      v.AddReservedWords("else","pi");
 /*     v.AddReservedWords("true", "false", "null", "if", "else",
                         "while", "for", "foreach", "in",
                         "switch", "case", "default", "break",
                         "continue", "return", "function", "is",
                         "pre", "post", "invariant", "new"); */

      StringLiteral s = new StringLiteral("String", TermOptions.None);
      s.AddStartEnd("'", ScanFlags.DisableEscapes);
      s.AddStartEnd("\"", ScanFlags.DisableEscapes);

      Terminal dot = Symbol(".", "dot");
      Terminal less = Symbol("<");
      Terminal greater = Symbol(">");
      Terminal divOp = Symbol("div");
      Terminal modOp = Symbol("mod");
      Terminal LSb = Symbol("[");
      Terminal RSb = Symbol("]");
      Terminal LCb = Symbol("(");
      Terminal RCb = Symbol(")");
      Terminal RFb = Symbol("}");
      Terminal LFb = Symbol("{");
      Terminal comma = Symbol(",");
      Terminal semicolon = Symbol(";");
      Terminal colon = Symbol(":");

      #endregion

      #region 2. Non-terminals
      NonTerminal BlockBegin = new NonTerminal("BlockBegin");
      NonTerminal BlockEnd = new NonTerminal("BlockEnd");
      NonTerminal Assignment = new NonTerminal("Assignment");
      NonTerminal AssignmentOp = new NonTerminal("AssignmentOp");
      
      #region 2.1 Expressions
      NonTerminal Expr = new NonTerminal("Expr");
      NonTerminal BinOp = new NonTerminal("BinOp");
      NonTerminal LUnOp = new NonTerminal("LUnOp");
      NonTerminal RUnOp = new NonTerminal("RUnOp");
      NonTerminal BoolOp = new NonTerminal("BoolOp");
      #endregion

      #region 2.2 QualifiedName
      //Expression List:  expr1, expr2, expr3, ..
      NonTerminal ExprList = new NonTerminal("ExprList");
      NonTerminal ArrayExprList = new NonTerminal("ArrayExprList");

      //A name in form: a.b.c().d[1,2].e ....
      NonTerminal QualifiedName = new NonTerminal("QualifiedName");
      NonTerminal ArrayExpression = new NonTerminal("ArrayExpression");
      NonTerminal FunctionExpression = new NonTerminal("FunctionExpression");
      #endregion

      #region 2.3 Statement
      NonTerminal Statement = new NonTerminal("Statement");
      NonTerminal Statements = new NonTerminal("Statements");

      //Block
      NonTerminal CompoundStatement = new NonTerminal("CompoundStatement");
      #endregion

      #region 2.4 Program and Functions
      NonTerminal Prog = new NonTerminal("Prog");
      NonTerminal ParameterList = new NonTerminal("ParamaterList");
      NonTerminal SwitchStatement = new NonTerminal("SwitchStatement");
      NonTerminal SwitchStatements = new NonTerminal("SwitchStatements");
      #endregion

      #endregion

      #region 3. BNF rules
      BlockBegin.Rule = LFb | "begin";
      BlockEnd.Rule = RFb | "end";
      Assignment.Rule = QualifiedName + AssignmentOp + Expr | QualifiedName + RUnOp;
      AssignmentOp.Rule = Symbol("=") | ":=" | "+=" | "-=" | "*=" | "/=" | "&=" | "|=" | "^=";
      #region 3.1 Expressions
      Expr.Rule = Symbol("true")
                  | "false"
                  | s
                  | n
                  | QualifiedName
                  | FunctionExpression
      			  | Expr + BoolOp + Expr
                  | Expr + BinOp + Expr
                  | LUnOp + Expr
                  | Expr + RUnOp
                  | LCb + Expr + RCb
                  ;

      BinOp.Rule = Symbol("+") | "-" | "*" | "/" | "^"| divOp | modOp | "&" | "|"
                  | "="| "==" | "!=" | greater | less 
                  | ">=" | "<=" | "<<" | ">>"; 
      
      BoolOp.Rule = Symbol("&&") | "||" | "^^" | "and" | "or" | "xor";

      LUnOp.Rule = Symbol("-") | "+" | "!" | "not" | "~";
      RUnOp.Rule = Symbol("++") | "--";

      #endregion

      #region 3.2 QualifiedName
      ArrayExpression.Rule = QualifiedName + LSb + ArrayExprList + RSb;
      FunctionExpression.Rule = v + LCb + ExprList.Q() + RCb;

      QualifiedName.Rule = v | QualifiedName + dot + v | ArrayExpression | ArrayExpression + dot + v; 


      ExprList.Rule = Expr.Plus(comma);
      ArrayExprList.Rule = Expr | Expr+comma+Expr;
      #endregion

      #region 3.3 Statement
      Statement.Rule =  "var" +v.Plus(comma)
      	| "globalvar" +v.Plus(comma)
      	| "if" + Expr + Statement + WithQ("else" + Statement)
      	| "if" + Expr + "then" + Statement + WithQ("else" + Statement)
      	| "repeat" + Expr + Statement
      	| "do" + Statement + "until" +Expr
        | "while" + Expr + Statement             
        | "for" + LCb + Assignment + semicolon + Expr + semicolon + Assignment + RCb + Statement
      	| "switch" + LCb + Expr + RCb + LFb + SwitchStatements + RFb
      	| CompoundStatement
      	| Assignment
      	| FunctionExpression
      	| "break"
      	| "continue"
      	| "exit"
      	| "return" + Expr + semicolon
      	| Statement + semicolon;
      	
      Statements.Rule = Statement.Star();
      CompoundStatement.Rule = BlockBegin + Statements + BlockEnd;

      SwitchStatement.Rule = Symbol("case") + Expr + colon + Statements | "default" + colon + Statements;
      SwitchStatements.Rule = SwitchStatement.Star();

      #endregion

      #region 3.4 Prog
      Prog.Rule = Statement.Star() + Eof;

      Terminal Comment = new CommentTerminal("Comment", "/*", "*/");
      ExtraTerminals.Add(Comment);
      Terminal LineComment = new CommentTerminal("LineComment", "//", "\n");
      ExtraTerminals.Add(LineComment);
      #endregion
      #endregion

      #region 4. Set starting symbol
      this.Root = Prog; // Set grammar root
      #endregion

      #region 5. Operators precedence      
      RegisterOperators(1, "||", "or", "&&", "and", "^^", "xor");
      RegisterOperators(2, "=", "==", "!=", ">", "<" , ">=", "<=");
      RegisterOperators(3, "|", "&", "^");
      RegisterOperators(4, "<<",">>");
      RegisterOperators(5, "+", "-");
      RegisterOperators(6, "*", "/", "div", "mod");
      
      RegisterOperators(7, "not", "!");
      RegisterOperators(8, "~", "++", "--" );
      RegisterOperators(9, ".");

      //RegisterOperators(10, Associativity.Right, ".",",", ")", "(", "]", "[", "{", "}");
      //RegisterOperators(11, Associativity.Right, "else");
      #endregion

      #region 6. Punctuation symbols
      RegisterPunctuation( "(", ")", "[", "]", "{", "}", ",", ";" );
      #endregion
    }

  }
}
